#define _REAL_ double precision

! This will be merged into pb_iimdrv()
!
subroutine IIM(gox,goy,goz,xm,ym,zm,lvlst,insas,nbnd,iepsav,epsin,epsout,bv,u,u0,accept,h,&
               atmfirst,atmlast,bcopt,solvopt)
    
!    xs <- gox, ys <- goy, zs <- goz
!    l  <- xm , m  <- ym , n  <- zm
!    bi <- epsin, bo <- epsout
!    u  <- phi, u0 <- xs
    
   implicit none
    
   ! passed variables
      
   _REAL_  gox,goy,goz,h,accept,epsout,epsin
   integer xm,ym,zm,nbnd,atmfirst,atmlast,bcopt,solvopt
   integer insas(0:xm+1,0:ym+1,0:zm+1),iepsav(1:4,1:xm*ym*zm)
   _REAL_  bv(xm,ym,zm),u0(xm,ym,zm),u(xm,ym,zm)
   _REAL_  lvlst(0:xm+1,0:ym+1,0:zm+1)
    
   ! common variables
    
   _REAL_ xs,ys,zs,xf,yf,zf,hx,hy,hz,hmax,bi,bo
   integer l, m, n, nirreg
   common /lmn/ l, m, n, nirreg
   common /xyz/ xs, xf, ys, yf, zs, zf
   common /hxyz/ hx,hy,hz,hmax
   common /para/ bi,bo

#  include "pb_constants.h"

   ! local variables
      
   _REAL_, parameter :: eps0 = 8.8542D-12 / (1.6022D-19)**2 /  &
                               (1.00D+10)**3 * (1.00D+12)**2 * 1.6606D-27
   integer, parameter :: nq=27
   integer i,j,k,IFAIL,IFAIL2,ii,ir,nc,nz_num
   _REAL_ beta_max,rhs
   _REAL_ coe1(7), coe2(27)
   _REAL_ fw

   _REAL_,allocatable :: x(:),y(:),z(:),cirreg(:,:)
   integer,allocatable :: index(:,:,:),index2(:,:,:)
   _REAL_,allocatable :: phi(:,:,:)
   _REAL_,allocatable :: wp(:),qp(:)
   _REAL_,allocatable :: q0p(:),qyp(:),qzp(:)
   _REAL_,allocatable :: w0p(:),wyp(:),wzp(:)
   _REAL_,allocatable :: wyyp(:),wzzp(:),wyzp(:)
   _REAL_,allocatable :: wcoe(:,:),wxcoe(:, :),wycoe(:, :)
   _REAL_,allocatable :: wzcoe(:,:),wxxcoe(:, :),wyycoe(:, :)
   _REAL_,allocatable :: wzzcoe(:, :),wxycoe(:, :)
   _REAL_,allocatable :: wxzcoe(:, :),wyzcoe(:, :)
   _REAL_,allocatable :: sss1(:),sss2(:)
   _REAL_,allocatable :: c(:,:,:,:), c2(:, :)
   _REAL_,allocatable :: f(:,:,:)

   ! interfacing variables 

   l = xm; m = ym; n = zm; nirreg = nbnd
   xs = gox; ys = goy; zs = goz
   hx = h; hy = h; hz = h; hmax = h
   bi = epsin; bo = epsout

   ! allocating working arrays

   allocate (cirreg(1:nbnd,1:15)) ! geometrical parameters at irregular points
   allocate (index(1:l,1:m,1:n), index2(1:l,1:m,1:n))
   allocate (x(0:l+1), y(0:m+1), z(0:n+1))
   allocate (phi(0:l+1,0:m+1,0:n+1))
   allocate (wp(nbnd), qp(nbnd))  ! jump conditions at irregular points
   allocate (q0p(nbnd),qyp(nbnd),qzp(nbnd)) ! tangential derivatives of field jump conditions
   allocate (w0p(nbnd),wyp(nbnd),wzp(nbnd)) ! tangential derivatives of potential jump conditions
   allocate (wyyp(nbnd),wzzp(nbnd),wyzp(nbnd)) ! second derivatives of potential jump conditions
   allocate (wcoe(nbnd,nq),wxcoe(nbnd, nq),wycoe(nbnd, nq)) ! coefficients obtained by SVD
   allocate (wzcoe(nbnd,nq),wxxcoe(nbnd, nq),wyycoe(nbnd, nq))
   allocate (wzzcoe(nbnd, nq),wxycoe(nbnd, nq))
   allocate (wxzcoe(nbnd, nq),wyzcoe(nbnd, nq))
   allocate (sss1(nbnd),sss2(nbnd)) !dummy arrays
   allocate (c(l,m,n,7), c2(nbnd, 27))
   allocate (f(l,m,n))
    
   ! setting coordinates of grid points
    
   do i = 0, l+1
      x(i) = xs + i*hx
   end do
   do j = 0, m+1
      y(j) = ys + j*hy
   end do
   do k = 0, n+1
      z(k) = zs + k*hz
   end do
   !write(99,*) x(1:l),y(1:m),z(1:n)
   
   ! set up the level set function

   phi(1:l,1:m,1:n) = lvlst(1:l,1:m,1:n)
   !do k =1, n; do j =1, m; do i = 1, l
   !   write(99,*) i,j,k,phi(i,j,k)
   !end do;end do;end do
   
   ! setting up index
   ! index = 1 for interior regular grid points;
   ! index = 2 for interior irregular grid points;
   ! index = 3 for boundary grid points right on the interface;
   ! index = 4 for exterior irregular grid points;
   ! index = 5 for exterior regular grid points;
   do k = 1, n
      do j = 1, m
         do i = 1, l
            !write(93,*),i,j,k,insas(i,j,k),phi(i,j,k)
            if ( insas(i,j,k) > 0 ) then
               index(i,j,k) = 1
            else
               index(i,j,k) = 5
            end if
         end do
      end do
   end do
   
   do ii = 1, nbnd
      i = iepsav(1,ii); j = iepsav(2,ii) ; k = iepsav(3,ii)
      !write(86,*) i,j,k,insas(i,j,k)
      if ( phi(i,j,k) > 0 ) then
         index(i,j,k) = 4
      else if ( phi(i,j,k) < 0 ) then
         index(i,j,k) = 2
      else
         index(i,j,k) = 3
      end if
   end do
   !do k =1, n; do j =1, m; do i = 1, l
   !   write(99,*) i,j,k,index(i,j,k)
   !end do;end do;end do
   
   ! setting up index2, the address index of irregular grid points
   !            cirreg, projection points, its transformation matrix & curvature

   call indexg(nbnd,x,y,z,index,phi, index2,cirreg)
   !do k =1, n; do j =1, m; do i = 1, l
   !   write(99,*) i,j,k,index2(i,j,k)
   !end do;end do;end do
   !do i = 1, nbnd
   !   write(99,*) i
   !   write(99,*) cirreg(i,1:15)
   !end do
   
   ! setting up the jump conditions at the projection points of the irreular (boundary) grid points

   call prodis(bcopt, atmfirst, atmlast, nbnd, cirreg, wp, qp)
   !do i = 1, nbnd
   !   write(98,*) i,wp(i),qp(i)
   !end do
    
   ! calculate the first and second derivatives of the jump conditions in the
   ! surface tangential directions in the local coordinate system
   ! step 1: this is the first derivatives

   call coed20(nbnd,nq,index2,cirreg, &
               wcoe,wxcoe,wycoe,wzcoe,wxxcoe,wyycoe,wzzcoe,wxycoe,wxzcoe,wyzcoe)
   call qint(nbnd,nq,index,index2,cirreg,qp, &
             wcoe,wycoe,wzcoe,q0p,qyp,qzp)
   call wint(nbnd,nq,index,index2,cirreg,wp, &
             wcoe,wycoe,wzcoe,wyycoe,wzzcoe,wyzcoe,w0p,wyp,wzp,wyyp,wzzp,wyzp)
    
   ! step 2: this is the second derivatives
    
   call coed6(nbnd,nq,index2,cirreg, &
              wcoe,wxcoe,wycoe,wzcoe,wxxcoe,wyycoe,wzzcoe,wxycoe,wxzcoe,wyzcoe)
   call wint(nbnd,nq,index,index2,cirreg,wp, &
             wcoe,wycoe,wzcoe,wyycoe,wzzcoe,wyzcoe,w0p,sss1,sss2,wyyp,wzzp,wyzp)
   !write(200,*) wcoe,wycoe,wzcoe,q0p,qyp,qzp,wyycoe,wzzcoe,wyzcoe,w0p,wyp,wzp,wyyp,wzzp,wyzp
   !write(200,*) q0p,qyp,qzp,w0p,wyp,wzp,wyyp,wzzp,wyzp
    
   ! setting up linear system coefficient matrix
    
   beta_max = max(bi,bo)
   nz_num = 0
   do k=1,n
      do j=1,m
         do i=1,l
            !write(200,*),i,j,k,index(i,j,k),nz_num
            if ( index(i,j,k).eq.1 ) then
               do ii = 2, 7
                  c(i,j,k,ii) = epsin/h/h
               end do
               if ( i == 1 ) c(i,j,k,2) = 0.d0
               if ( i == l ) c(i,j,k,3) = 0.d0
               if ( j == 1 ) c(i,j,k,4) = 0.d0
               if ( j == m ) c(i,j,k,5) = 0.d0
               if ( k == 1 ) c(i,j,k,6) = 0.d0
               if ( k == n ) c(i,j,k,7) = 0.d0
               c(i,j,k,1) = 6.d0*epsin/h/h
               ii = i+(j-1)*l+(k-1)*l*m
               f(i,j,k)   = bv(i,j,k)/h/h/h*FOURPI
               do ii = 1 , 7
                  if ( abs(c(i,j,k,ii)) > 1.d-10 ) nz_num=nz_num+1
               end do
            else if (index(i,j,k).eq.5 ) then
               do ii = 2, 7
                  c(i,j,k,ii) = epsout/h/h
               end do
               if ( i == 1 ) c(i,j,k,2) = 0.d0
               if ( i == l ) c(i,j,k,3) = 0.d0
               if ( j == 1 ) c(i,j,k,4) = 0.d0
               if ( j == m ) c(i,j,k,5) = 0.d0
               if ( k == 1 ) c(i,j,k,6) = 0.d0
               if ( k == n ) c(i,j,k,7) = 0.d0
               c(i,j,k,1) = 6.d0*epsout/h/h
               ii = i+(j-1)*l+(k-1)*l*m
               f(i,j,k)   = bv(i,j,k)/h/h/h*FOURPI
               !if (i /= 1 .and. i /= l .and. &
               !    j /= 1 .and. j /= m .and. &
               !    k /= 1 .and. k /= n)
               do ii = 1, 7
                  if ( c(i,j,k,ii ) > 1.d-10 ) nz_num=nz_num+1
               end do
!print *,c(i,j,k,1:7)
!print *,"==========",i,j,k,"========="
!print *,c(i,j,k,1:7),f(i,j,k)
!              do nc=1,7
!                 c(i,j,k,nc) = coe1(nc)
!              end do          
!              f(i,j,k) = rhs
!print *,i,j,k, f(i,j,k)
            else
               ii = i+(j-1)*l+(k-1)*l*m
               !write(100,*), i,j,k, sngl(bv(ii)), index(i,j,k)
               !write(200,*) i,j,k,index(i,j,k),'using irre31'
               call irre31(IFAIL, &
                           i,j,k,index(i,j,k),beta_max,x,y,z,phi,index, &
                           q0p,qyp,qzp,w0p,wyp,wzp,wyyp,wzzp,wyzp,  &  
                           nq,nbnd,index2,cirreg,wp,qp,coe2,rhs)
               if (IFAIL.gt.10) then
                  !write(200,*) i,j,k,index(i,j,k),'using irre32'
                  call irre32(IFAIL2, &
                              i,j,k,index(i,j,k),beta_max,x,y,z,phi,index, &
                              q0p,qyp,qzp,w0p,wyp,wzp,wyyp,wzzp,wyzp, &
                              nq,nbnd,index2,cirreg,wp,qp,coe2,rhs)
               endif
               ir = index2(i,j,k)
!print *,"=== i ====",i,j,k,"========="
!print *,insas(i,j,k),index(i,j,k)
!print *,x(i),y(j),z(k)
!print *,cirreg(ir,1:15)
!print *,'----------------------------'
!print *,w0p(ir),wyp(ir),wzp(ir)
!print *,wyyp(ir),wzzp(ir),wyzp(ir)
!print *,q0p(ir),qyp(ir),qzp(ir)
!print *,'----------------------------'

               do nc = 1, 27
                  c2(ir,nc) = coe2(nc)
               end do
               f(i,j,k) = rhs
               do ii = 1, 27
                  if ( abs(c2(ir,ii)) > 1.d-10 ) nz_num=nz_num+1
               end do
!print *,c2(ir,1:27),f(i,j,k)
            endif
         end do
      end do
   end do

   ! cleaning up working arrays
   
   deallocate (x, y, z)
   deallocate (phi)
   deallocate (cirreg) ! geometrical parameters at irregular points
   deallocate (wp, qp) ! jump condition at irregular points
   deallocate (q0p,qyp,qzp) ! tangential derivatives of field jump conditions
   deallocate (w0p,wyp,wzp) ! tangential derivatives of potential jump conditions
   deallocate (wyyp,wzzp,wyzp) ! second derivatives of potential jump conditions
   deallocate (wcoe,wxcoe,wycoe) ! coefficients obtained by SVD
   deallocate (wzcoe,wxxcoe,wyycoe)
   deallocate (wzzcoe,wxycoe)
   deallocate (wxzcoe,wyzcoe)
   deallocate (sss1,sss2) !dummy arrays

   ! entering the linear system solver

   if ( solvopt == 1 ) then
      ! algebraic multigrid
      !call amg(l,m,n,nbnd,c,c2,index,index2,f,u,u0,accept) ! should be modified
      write(6,*) "AMG Driver is retired."; call mexit(6,1)
   else if ( solvopt == 2 ) then
      ! ILU preconditioned GMRES
      call gmres(l,m,n,nbnd,nz_num,c,c2,index,index2,f,u,u0,accept)
   else if ( solvopt == 3 ) then
      ! ILU preconditioned BiCG
      call bicg(l,m,n,nbnd,nz_num,c,c2,index,index2,f,u,u0,accept)
   else
      write(6,*) "Unsupported Solver option"
   end if

   ! converting back to the PBSA unit for potential

   do k=1, n
      do j=1, m
         do i=1, l
!print *,i,j,k,index(i,j,k)
!          if (index(i,j,k) < 3) u(i,j,k) = u(i,j,k) + fw(x(i),y(j),z(k))
           u(i,j,k) = u(i,j,k) * INV_FOURPI / eps0
!          write (85,*),i,j,k,u(i,j,k) / 4.0 / pi / eps0
!          write (85,*),i,j,k,u(i,j,k)
         end do
      end do
   end do  
   
   deallocate (index, index2)
   deallocate (c, c2)
   deallocate (f)


end subroutine IIM
